import * as THREE from 'three';
import Ball from "./ball"

class BallCollisionControl {
  settings: IBallCollision.Settings = {
    element: document.body,
    number: 10,
    colorSet: ["#fff000"],
    sizeRange: [.5, 1],
    opacityRange: [.5, .8],
    speed: .1
  }
  destroyed: boolean = false
  wrapperRect: IBallCollision.WrapperRect
  cameraRect: IBallCollision.CameraRect = {x: 0, y: 0}
  ballList: Ball[] = []
  scene: THREE.Scene
  renderer: THREE.WebGLRenderer 
  camera: THREE.PerspectiveCamera
  constructor(settings: Partial<IBallCollision.Settings>) {
    this.settings = Object.assign(this.settings, settings);
    this.wrapperRect = this.getElementRect(this.settings.element)
    this.scene = new THREE.Scene()
    this.camera = new THREE.PerspectiveCamera(75, this.wrapperRect.width / this.wrapperRect.height, 1, 1000)
    this.renderer = new THREE.WebGLRenderer({
      antialias: true, alpha: true
    })
  }
  getCameraRect(camera: THREE.PerspectiveCamera) {
    let y = Math.tan(THREE.MathUtils.degToRad(camera.fov / 2)) * camera.position.z
    let x = y * camera.aspect
    return {x, y}
  }
  getElementRect(element: HTMLElement): IBallCollision.WrapperRect {
    return {
      width: element.offsetWidth, height: element.offsetHeight,
      aspect: element.offsetWidth / element.offsetHeight
    }
  }
  install(): BallCollisionControl {
    this.camera.position.set(0, 0, 20)
    this.cameraRect = this.getCameraRect(this.camera)
    this.camera.lookAt(0, 0, 0)
    this.renderer.setSize( this.wrapperRect.width, this.wrapperRect.height );
    this.settings.element.appendChild( this.renderer.domElement );
    this.setSceneBackgroundColor()
    this.createBalls()
    this.render()
    return this
  }
  setSceneBackgroundColor() {
    if (this.settings.backgroundColor) {
      this.scene.background = new THREE.Color(this.settings.backgroundColor)
    }
  }
  createBalls() {
    new Array(this.settings.number).fill(0).forEach(() => {
      const ball = new Ball({
        ...this.settings,
        cameraRect: this.cameraRect,
        showDirectionArrow: false,
      })
      this.scene.add(ball.object)
      this.ballList.push(ball)
    })
  }
  render() {
    if (this.destroyed) return
    requestAnimationFrame(() => this.render());
	  this.renderer.render( this.scene, this.camera )
    this.ballList.forEach(ball => {
      ball.run()
    })
  }
  uninstall(): void {
    this.destroyed = true
    if (this.renderer.domElement && this.renderer.domElement.parentElement) {
      this.renderer.domElement.parentElement.removeChild(this.renderer.domElement)
    }
    this.renderer.dispose()
  }
}

export default BallCollisionControl;